package by.parfen.game.servlet;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.json.JsonObject;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import by.parfen.game.datamodel.Game;
import by.parfen.game.datamodel.GameMessage;
import by.parfen.game.datamodel.Invitation;
import by.parfen.game.datamodel.Player;
import by.parfen.game.service.GameMessageService;
import by.parfen.game.service.GameServiceListener;
import by.parfen.game.service.impl.GameMessageServiceImpl;
import by.parfen.game.service.impl.GameServiceImpl;
import by.parfen.game.websocket.WebsocketClientEndpoint;

@WebServlet("/MainServlet")
public class MainServlet extends HttpServlet {
	private static final long serialVersionUID = 4653134542937073831L;

	private static final Logger LOGGER = LoggerFactory.getLogger(MainServlet.class);

	private static final String SERVLET_PAGE = "/duelgame/MainServlet";

	private static final String PAGE_PARAM_NAME = "page";
	private static final String USERNAME_PARAM_NAME = "username";
	private static final String PLAYER_PARAM_NAME = "player";
	private static final String MESSAGE_PARAM_NAME = "message";
	private static final String AVAILABLE_PLAYERS_PARAM_NAME = "availablePlayers";
	private static final String INVITATIONS_PARAM_NAME = "invitations";
	private static final String INVITATION_PARAM_NAME = "invitation";
	private static final String GAMES_PARAM_NAME = "games";
	private static final String ACTION_PARAM_NAME = "action";
	private static final String ID_PARAM_NAME = "id";

	private static final String ACTION_JOIN = "join";
	private static final String ACTION_REJECT = "reject";

	private static final String LOGIN_PAGE = "login";
	private static final String LOGOUT_PAGE = "logout";
	private static final String INVITE_PAGE = "invite";
	private static final String GAMES_AREA_PAGE = "gamesArea";
	private static final String INVITATION_PAGE = "invitation";
	private static final String GAME_PAGE = "game";

	// private static final String GAMES_AREA_PAGE = "gamesAreaWithTags";
	private static final String TEMPLATE_PAGE_SUFFIX = ".jsp";

	private static final String WS_URL = "ws://localhost:8180/duelgame";
	private static final String WS_GAME_URL = WS_URL + "/websocket-game/root";

	private GameServiceImpl gameService;
	private GameServiceListener gameListener;
	private WebsocketClientEndpoint clientEndPoint;

	@Override
	public void init() throws ServletException {
		try {
			clientEndPoint = new WebsocketClientEndpoint(new URI(WS_GAME_URL));
		} catch (URISyntaxException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		gameService = new GameServiceImpl();

		gameListener = new GameServiceListener() {

			@Override
			public void onSignUp(Player player) {
				// TODO Auto-generated method stub
				LOGGER.info(String.format("onSignUp. Player: '%s'", player.getName()));

				// create test message
				GameMessage gameMessage = new GameMessage();
				gameMessage.setSubject("NEW_PLAYER");
				gameMessage.setContent(player.getName());

				GameMessageService gameMessageService = new GameMessageServiceImpl();
				JsonObject jsonMessage = gameMessageService.encode(gameMessage);

				// send message to websocket
				clientEndPoint.sendMessage(jsonMessage.toString());
			}

			@Override
			public void onLeave(Player player) {
				// TODO Auto-generated method stub
				LOGGER.info("onLeave.");
				LOGGER.info(String.format("onLeave. Player: '%s'", player));
			}

			@Override
			public void onInvite(Invitation invitation) {
				// TODO Auto-generated method stub
				LOGGER.info(String.format("onInvite, invitation: %s", invitation));
			}

			@Override
			public void onGameStart(Game game) {
				// TODO Auto-generated method stub
				LOGGER.info(String.format("onGameStart. Game: '%s'", game.getName()));
			}

			@Override
			public void onGameMove(Game game) {
				// TODO Auto-generated method stub
				LOGGER.info(String.format("onGameMove. Game: '%s'", game.getName()));
			}

			@Override
			public void onGameFinish(Long gameId) {
				// TODO Auto-generated method stub
				LOGGER.info(String.format("onGameFinish. Game id: '%s'", gameId));
			}

			@Override
			public void onCancel(Invitation invitation) {
				// TODO Auto-generated method stub
				LOGGER.info(String.format("onCancel invitation '%s'", invitation.getId()));
			}

			@Override
			public void onAccept(Invitation invitation) {
				// TODO Auto-generated method stub
				LOGGER.info(String.format("onAccept invitation '%s'", invitation.getId()));
			}
		};

		gameService.setListener(gameListener);
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		processRequest(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		processRequest(req, resp);
	}

	private void echoMap(Map<String, String[]> map) {
		for (Entry<String, String[]> entry : map.entrySet()) {
			String[] value = entry.getValue();
			System.out.println(entry.getKey() + "=" + java.util.Arrays.asList(value));
		}
	}

	private void echoEnumeration(Enumeration<String> enumeration) {
		while (enumeration.hasMoreElements()) {
			System.out.println(enumeration.nextElement());
		}
	}

	private void TestAndEchoParam(HttpServletRequest req, final String paramName) {
		// System.out.println(String.format("req.getParameter(%s) = %s",
		// paramName,
		// req.getParameter(paramName)));
		// System.out.println(String.format("req.getAttribute(%s) = %s",
		// paramName,
		// req.getAttribute(paramName)));
		// System.out.println(
		// String.format("req.getSession().getAttribute(%s) = %s", paramName,
		// req.getSession().getAttribute(paramName)));
	}

	protected void processRequest(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		Boolean redirect = false;

		System.out.print("req.getParameterMap(): ");
		echoMap(req.getParameterMap());
		System.out.print("req.getSession().getAttributeNames(): ");
		echoEnumeration(req.getSession().getAttributeNames());
		TestAndEchoParam(req, PAGE_PARAM_NAME);
		TestAndEchoParam(req, USERNAME_PARAM_NAME);
		TestAndEchoParam(req, AVAILABLE_PLAYERS_PARAM_NAME);
		TestAndEchoParam(req, INVITATIONS_PARAM_NAME);

		// Current page name by redirect or forwarding
		String page = req.getParameter(PAGE_PARAM_NAME);
		if (page == null) {
			page = (String) req.getAttribute(PAGE_PARAM_NAME);
		}
		page = (page == null) ? LOGIN_PAGE : page;

		// Destination page
		String nextPage = LOGIN_PAGE;

		// Check username
		String username = req.getParameter(USERNAME_PARAM_NAME);
		if (username == null) {
			username = (String) req.getAttribute(USERNAME_PARAM_NAME);
			if (username == null) {
				username = (String) req.getSession().getAttribute(USERNAME_PARAM_NAME);
			}
		}
		// and redirect if username value is invalid
		page = (username == null || username.isEmpty()) ? LOGIN_PAGE : page;

		String action = "";
		String invitationId = "";

		System.out.println("page: " + page);
		if (page.equals(INVITATION_PAGE)) {
			action = req.getParameter(ACTION_PARAM_NAME);
			if (action != null && (action.equals(ACTION_JOIN) || action.equals(ACTION_REJECT))) {
				invitationId = req.getParameter(ID_PARAM_NAME);
			} else if (action != null) {
				// invalid value
				LOGGER.info(String.format("Invalid value for action: '%s'", action));
				action = null;
			}
		}
		System.out.println("action: " + action);
		System.out.println("invitationId: " + invitationId);

		List<Player> availablePlayers = gameService.getAvailablePlayers(null);
		System.out.println("availablePlayers: " + availablePlayers.size());
		List<Invitation> invitations = gameService.getInvitationsToPlayer(username);
		System.out.println("invitations: " + invitations.size());

		req.setAttribute(MESSAGE_PARAM_NAME, "");
		switch (page) {
		case (LOGIN_PAGE):
			if (username == null || username.isEmpty()) {
				nextPage = LOGIN_PAGE;
			} else {
				System.out.println("gameService.signUp player with name: " + username);
				gameService.signUp(username);
				nextPage = GAMES_AREA_PAGE;
				req.getSession().setAttribute(USERNAME_PARAM_NAME, username);
				redirect = true;
			}
			break;

		case (LOGOUT_PAGE):
			gameService.leave(username);
			nextPage = LOGIN_PAGE;
			req.getSession().removeAttribute(USERNAME_PARAM_NAME);
			redirect = true;
			break;

		case (INVITATION_PAGE):
			if (action.equals(ACTION_JOIN)) {
				System.out.println("Try to Accept invitation with id = " + invitationId);
				gameService.accept(Long.parseLong(invitationId, 10));
				// TODO
				// go to the Game Page instead of the Games Area page
				nextPage = GAMES_AREA_PAGE;
			} else if (action.equals(ACTION_REJECT)) {
				System.out.println("Try to Cancel invitation with id = " + invitationId);
				gameService.cancel(Long.parseLong(invitationId, 10));
				nextPage = GAMES_AREA_PAGE;
			}
			redirect = true;
			break;

		case (INVITE_PAGE):
			String playerName = (String) req.getParameter(PLAYER_PARAM_NAME);
			if (playerName != null) {
				System.out.println("Try to invite playerName: " + playerName);
				gameService.invite(username, playerName);
				// gameService.leave(username);
				req.setAttribute(MESSAGE_PARAM_NAME, "Invited player: " + playerName);
			}
			nextPage = GAMES_AREA_PAGE;
			redirect = true;
			break;
		case (GAMES_AREA_PAGE):
			nextPage = GAMES_AREA_PAGE;
			break;
		}
		System.out.println("nextPage: " + nextPage);

		if (nextPage.equals(GAMES_AREA_PAGE)) {
			availablePlayers = gameService.getAvailablePlayers(username);
			invitations = gameService.getInvitationsToPlayer(username);
			req.setAttribute(AVAILABLE_PLAYERS_PARAM_NAME, availablePlayers);
			req.setAttribute(INVITATIONS_PARAM_NAME, invitations);
		}

		// add request attribute
		req.setAttribute(PAGE_PARAM_NAME, nextPage);
		System.out.println("redirect: " + redirect);
		if (redirect) {
			// 302-redirect
			resp.sendRedirect(getNextPageUrl(nextPage));
		} else {
			// and server side redirect
			RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(getTemplateName(nextPage));
			dispatcher.forward(req, resp);
		}

	}

	private String getTemplateName(String pagename) {
		return "/" + pagename + TEMPLATE_PAGE_SUFFIX;
	}

	private String getNextPageUrl(String pagename) {
		return SERVLET_PAGE + "?" + PAGE_PARAM_NAME + "=" + pagename;
	}

}
